#include <arpa/inet.h>
#include <stdio.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <string.h>
#include <stddef.h>

#include "log.h"


/** sockaddrcmp 比较两个sockaddr的地址是否相等
 *
 *
 */
int sockaddrcmp(const void *a, const void *b) {

	if (((struct sockaddr *)a)->sa_family != ((struct sockaddr *)b)->sa_family)
		return -1;
	switch (((struct sockaddr *)a)->sa_family) {
	case AF_INET:
		return memcmp(a + offsetof(struct sockaddr_in, sin_addr),
				b + offsetof(struct sockaddr_in, sin_addr),
				sizeof(((struct sockaddr_in *)a)->sin_addr));
		break;
	case AF_INET6:
		return memcmp(a + offsetof(struct sockaddr_in6, sin6_addr),
				b + offsetof(struct sockaddr_in6, sin6_addr),
				sizeof(((struct sockaddr_in6 *)a)->sin6_addr));
		break;
	default:
		break;
	}

	return -1;
}

/* sockaddr_ntop 将网络地址结构转换成易读的形式(可打印)保存到给定缓存中
 *
 * 参数：addr 网络地址结构 (value)
 *      buf 存放可打印地址的缓存 (result)
 *      buflen 缓存的最大长度 (value)
 *
 * 返回：成功返回缓存的可打印地址，失败返回NULL
 */
const char *sockaddr_ntop(const void *addr, char *buf, size_t buflen)
{
	uint16_t port;
	char pbuf[INET6_ADDRSTRLEN];
	const char *ret = buf;

	switch (((struct sockaddr *)addr)->sa_family) {
	case AF_INET:
		ret = inet_ntop(AF_INET, &((struct sockaddr_in *)addr)->sin_addr,
				pbuf, sizeof(pbuf));
		port = ntohs(((struct sockaddr_in *)addr)->sin_port);
		break;
	case AF_INET6:
		ret = inet_ntop(AF_INET6, &((struct sockaddr_in6 *)addr)->sin6_addr,
				pbuf, sizeof(pbuf));
		port = ntohs(((struct sockaddr_in6 *)addr)->sin6_port);
		break;
	default:
		snprintf(buf, buflen, "unknown address");
		return buf;
	}

	if (!ret) {
		LOG(LOG_DEBUG, "inet_ntop error: %s", strerror(errno));
		return NULL;
	}

#if _BSD_SOURCE || _XOPEN_SOURCE >= 500 || _ISOC99_SOURCE || _POSIX_C_SOURCE >= 200112L
		snprintf(buf, buflen, "%s.%hu", pbuf, port);
#else
		if (buflen < strlen(pbuf) + 7)
			return NULL;
		sprintf(buf, "%s.%d", pbuf, port);
#endif

	return buf;
}

/* sockname_ntop 获取socket的绑定地址并以易读的形式(可打印)保存在给定的缓存中
 *
 * 参数：sock 已连接的socket (value)
 *      buf 存放可打印地址的缓存 (result)
 *      buflen 缓存的最大长度 (value)
 *
 * 返回：成功返回缓存的可打印地址，失败返回NULL
 */
const char *sockname_ntop(int sock, char *buf, size_t buflen)
{
	struct sockaddr_storage addr;
	socklen_t addrlen = sizeof(addr);

	if (getsockname(sock, (struct sockaddr *)&addr, &addrlen) < 0) {
		LOG(LOG_WARNING, "sockname_ntop failed: getsockname error: %s", strerror(errno));
		return NULL;
	}

	return sockaddr_ntop(&addr, buf, buflen);
}

/* peername_ntop 获取已连接socket的对方地址并以易读的形式(可打印)保存在给定的缓存中
 *
 * 参数：sock 已连接的socket (value)
 *      buf 存放可打印地址的缓存 (result)
 *      buflen 缓存的最大长度 (value)
 *
 * 返回：成功返回缓存的可打印地址，失败返回NULL
 */
const char *peername_ntop(int sock, char *buf, size_t buflen)
{
	struct sockaddr_storage addr;
	socklen_t addrlen = sizeof(addr);

	if (getpeername(sock, (struct sockaddr *)&addr, &addrlen) < 0) {
		LOG(LOG_WARNING, "peername_ntop failed: getpeername error: %s", strerror(errno));
		return NULL;
	}

	return sockaddr_ntop(&addr, buf, buflen);
}
